package tragame.game;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

import tragame.interfaces.IMap;
import tragame.interfaces.IVillage;

/**
 * This class is the game map : basically an array of squares. This class is a
 * singleton
 * @author jing jing
 *
 */

public class Map extends UnicastRemoteObject implements IMap{

	private static final long serialVersionUID = 1L;
	
	private static Map instance = null;
	private int size;
	private Square[][] platform;
	private int nbVillages = 0;
	
	/**
	 * singleton definition
	 * @throws RemoteException 
	 *
	 */
	
	public static Map getInstance(int size) throws RemoteException{
		if(instance == null) {
			instance = new Map(size);
		}
		return instance;
	}
	
	/**
	 * Default constructor
	 * @param size of the map
	 */
	private Map(int size) throws RemoteException{
		this.size = size;
		initialize();
	}
	
	/**
	 * add a village on the map
	 * @param name of the village
	 * @param posX x-position on the map
	 * @param posY y-position on the map
	 * @return true : the village has been added
	 *         false: the village has not been added because there was another
	 *          village at this position or the map is full or the coordinates
	 *          are outside the map
	 * @throws RemoteException 
	 */
	public boolean addVillage(String player, String name, String race, int posX, int posY) throws RemoteException {
		if(this.nbVillages == (this.size * this.size) || posX>=this.size || posX<0 || posY>=this.size || posY<0) {
			return false;
		} else {
			if(this.platform[posX][posY] instanceof Field) {
				this.nbVillages += 1;
				this.platform[posX][posY] = new Village(player, name, race, (Field)this.platform[posX][posY]);
				return true;
			} else {
				return false;
			}
		}
	}
	
	/**
	 * add a village on the map automatically
	 * @param name of the village
	 * @param race of the player
	 * @return true  : the village has been added on the map
	 *         false : the village has not been added on the map because the map is full;
	 * @throws RemoteException 
	 */
	public boolean addVillage(String player, String name, String race) throws RemoteException {
		if(this.nbVillages == (this.size * this.size)) {
			return false;
		} else {
			final int attempts = 100;
			int i = 0; boolean placed = false;
			while(i<attempts && !placed) {
				int x = (int)(Math.floor(Math.random()*this.size));
				int y = (int)(Math.floor(Math.random()*this.size));
				if(this.platform[x][y].getClass().toString().equals("class game.Field")) {
					placed = true; this.nbVillages++;
					this.platform[x][y] = new Village(player, name, race, (Field)this.platform[x][y]);
				}
			}
			if(!placed) {
				i = 0;
				while(!placed) {
					int j = 0;
					while(!placed && j<this.size) {
						if(this.platform[i][j].getClass().toString().equals("class game.Field")) {
							placed = true; this.nbVillages++;
							this.platform[i][j] = new Village(player, name, race, (Field)this.platform[i][j]);
						}
						j++;
					}
					i++;
				}
			}
		}
		return true;
	}
	
	/**
	 * delete a village from the map and replace it with a field
	 * @param posX x-position on the map 
	 * @param posY y-position on the map
	 * @return true  : the village has been replaced
	 *         false : No village has been deleted because it does not exist
	 *         or the coordinates are outside the map
	 * @throws RemoteException 
	 */
	public boolean deleteVillage(int posX, int posY) throws RemoteException {
		if(posX>=this.size || posX<0 || posY>=this.size || posY<0) {
			return false;
		} else {
			if(this.platform[posX][posY] instanceof Village) {
				this.nbVillages -= 1;
				this.platform[posX][posY] = new Field(posX, posY);
				return true;
			} else {
				return false;
			}
		}
	}
	
	/**
	 * get the size of the map
	 * @return the size of the map
	 */
	public int getSize() {
		return this.size;
	}
	
	/**
	 * get the number of villages on the map
	 * @return the number of villages
	 */
	public int getNbVillages() {
		return this.nbVillages;
	}
	
	/**
	 * get the village of the given name
	 * @param posX x-position on the map
	 * @param posY y-position on the map
	 * @return the wanted village or null if the square at the given position is not a village or 
	 * if the positions are outside the map
	 */
	public IVillage getVillage(int posX, int posY) {
		if(posX>=this.size || posX<0 || posY>=this.size || posY<0) {
			return null;
		} else {
			if(this.platform[posX][posY] instanceof Village) {
				return (IVillage)this.platform[posX][posY];
			} else {
				return null;
			}
		}
	}
	
	/**
	 * get the Field at the given position
	 * @param posX x-position on the map
	 * @param posY y-position on the map
	 * @return the wanted field or null if the square at the given position is not a field or 
	 * if the positions are outside the map
	 */
	public Field getField(int posX, int posY) {
		if(posX>=this.size || posX<0 || posY>=this.size || posY<0) {
			return null;
		} else {
			if(this.platform[posX][posY] instanceof Field) {
				return (Field)this.platform[posX][posY];
			} else {
				return null;
			}
		}
	}
	
	private void initialize() throws RemoteException {
		this.platform = new Square[this.size][this.size];
		for(int i = 0; i<this.size; i++) {
			for(int j = 0; j<this.size; j++) {
				this.platform[i][j] = new Field(i, j);
			}
		}
	}

}
